package com.jellyfishumbrella;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;

import megamu.mesh.Delaunay;
import megamu.mesh.Edge;
import megamu.mesh.Region;
import megamu.mesh.Voronoi;
import be.humphreys.simplevoronoi.Cell;
import be.humphreys.simplevoronoi.LandscapeSite;

import com.jellyfishumbrella.GL.Normal;
import com.jellyfishumbrella.noise.Noise;

public class LandscapeData {
	public double							borderRadius	= 100 * GameLogic.SCALING;
	public Voronoi							voronoi;
	public Delaunay							delaunay;
	double									numBorderSites	= 10;
	int										landscapePoints;
	VoronoiInput<Site>						baseSites;

	Random									r				= new Random();
	private static long						SEED			= GameLogic.getTime();					// 18;
	private static Noise					n				= new Noise(SEED, 10000);
	Region[]								regions;
	ArrayList<Edge>							edges;
	LinkedHashMap<Site, Region>				allRegions;
	LinkedHashMap<Site, Region>				landRegions;
	LinkedHashMap<Site, Region>				waterRegions;
	public LinkedHashMap<String, Vertex>	mergedVerts		= new LinkedHashMap<String, Vertex>();
	LinkedHashMap<String, Edge>				mergedEdges		= new LinkedHashMap<String, Edge>();

	public LinkedHashMap<String, Corner>	allCorners		= new LinkedHashMap<String, Corner>();
	ArrayList<Corner>						drawCorners		= new ArrayList<Corner>();
	public double							maxAlt			= 0;

	short									allCornerIndex	= 0;

	public LandscapeData() {
		SEED = 18;// new Date().getTime();
		n = new Noise(SEED, 1000);
	}

	public void createlandscape(double borderRadius, int numLandscapePoints) {
		this.borderRadius = borderRadius;
		this.landscapePoints = numLandscapePoints;

		baseSites = new VoronoiInput<Site>();

		createBorderSites(numBorderSites, true);
		createIslandSites(4);

		while (baseSites.size() < numLandscapePoints) {
			voronoi = new Voronoi();
			delaunay = new Delaunay();

			voronoi.generateVoronoi(baseSites);
			delaunay.generateDelaunay(baseSites);
			populateRegionsAndEdges();
			populateSiteLinks();

			mergeCornersAndEdges(0.001);

			baseSites = subdivideVoronoi();
		}
/*
 * //for (int i = 0; i < 2; i++) {
 * relaxSites();
 * voronoi.generateVoronoi(baseSites);
 * delaunay.generateDelaunay(baseSites);
 * populateRegionsAndEdges();
 * populateSiteLinks();
 * //}
 */

		makeLandorWater();
		expandBorder();

		divideLandAndWater();
		applyElevations();
		// determineDownHill();
		// placeRivers(30);

		double scaler = 200.0 / maxAlt;
		System.out.println(scaler);
		scaleElevations(scaler);

		// makeGLTriangleFans();

		computeNormals();

		glScale();

		System.out.println(maxAlt);

	}

	public void createBorderSites(double numBorderSites, boolean isRadial) {
		if (isRadial) {
			int i = 0;
			// for (int i = 0; i < 3; i++) {
			double radius = (borderRadius + 100 * (i));
			for (double angle = 0; angle < 360.0; angle += 360.0 / numBorderSites) {
				double X = radius * Math.cos(Math.toRadians(angle));
				double Z = radius * Math.sin(Math.toRadians(angle));
				Site newSite = new Site(X + r.nextDouble(), Z + r.nextDouble());

				newSite.y = 0;
				newSite.isBorder = true;
				baseSites.add(newSite);
			}
			// }
		} else {
			for (double i = 0; i < numBorderSites; i++) {
				double xStep = i / (numBorderSites - 1.0);
				LandscapeSite newSite = new LandscapeSite(borderRadius * xStep + r.nextDouble() - borderRadius / 2.0, 0 + r.nextDouble() - borderRadius / 2.0);
				newSite.y = 0;
				newSite.isBorder = true;
				baseSites.add(newSite);
				newSite = new LandscapeSite(borderRadius * xStep + r.nextDouble() - borderRadius / 2.0, borderRadius + r.nextDouble() - borderRadius / 2.0);
				newSite.y = 0;
				newSite.isBorder = true;
				baseSites.add(newSite);
			}
			for (double j = 0; j < numBorderSites; j++) {
				double yStep = j / (numBorderSites - 1.0);
				LandscapeSite newSite = new LandscapeSite(0 + r.nextDouble() - borderRadius / 2.0, borderRadius * yStep + r.nextDouble() - borderRadius / 2.0);
				newSite.y = 0;
				newSite.isBorder = true;
				baseSites.add(newSite);
				newSite = new LandscapeSite(borderRadius + r.nextDouble() - borderRadius / 2.0, borderRadius * yStep + r.nextDouble() + r.nextDouble() - borderRadius / 2.0);
				newSite.y = 0;
				newSite.isBorder = true;
				baseSites.add(newSite);
			}
		}
		System.out.println("# of sites: " + baseSites.size());
	}

	public void createIslandSites(int steps) {
		double BR = borderRadius * 0.85;
		for (int i = 0; i < steps; i++) {
			double X = BR - r.nextDouble() * BR * 2;
			double Z = BR - r.nextDouble() * BR * 2;
			double radius = Math.sqrt(X * X + Z * Z);
			if (radius < BR) {
				Site newSite = new Site(X + r.nextDouble(), Z + r.nextDouble());
				newSite.isBorder = false;
				if (r.nextDouble() < 0.25) {
					newSite.isWater = true;
				} else {
					newSite.isWater = false;
				}
				baseSites.add(newSite);
			}
		}
		System.out.println("# of sites: " + baseSites.size() + "    " + steps);
	}

	public void populateRegionsAndEdges() {
		regions = voronoi.getRegions();
		edges = voronoi.getEdges();
	}

	public void populateSiteLinks() {
		for (int i = 0; i < baseSites.size(); i++) {
			Site currSite = (Site) baseSites.get(i);
			ArrayList<Integer> links = delaunay.getLinked(i);
			currSite.links = new ArrayList<Site>();

			for (int j = 0; j < links.size(); j++) {
				currSite.links.add((Site) baseSites.get(links.get(j)));
			}
		}
	}

	public VoronoiInput subdivideVoronoi() {
		VoronoiInput subDSites = new VoronoiInput<Site>();

		for (int i = 0; i < baseSites.size(); i++) {
			Site currSite = (Site) baseSites.get(i);
			Region currRegion = currSite.region;
			// subDSites.add(currSite);

			if (!currSite.isBorder && currRegion!=null && currRegion.vertCount()>0) {
				ArrayList<Vertex> verts = currRegion.getVerts();
				for (int j = 0; j < currRegion.vertCount(); j++) {
					Vertex currVert = verts.get(j);

					boolean noBorderNeighbors = true;
					Site newSite = new Site(currVert);
					newSite.isWater = currSite.isWater;

					for (int k = 0; k < currVert.regions.size(); k++) {
						if (newSite.isBorder) {
							noBorderNeighbors = false;
							break;
						}
						if (currVert.regions.get(k).site.isWater != currSite.isWater) {
							if (r.nextDouble() < 0.25) {
								newSite.isWater = true;
							} else {
								newSite.isWater = false;
							}
							break;
						}
					}
					if (noBorderNeighbors) {
						subDSites.add(newSite);
					}
				}
			}
		}

		return subDSites;
	}

	public void relaxSites() {
		for (int i = 0; i < regions.length; i++) {
			Region currRegion = regions[i];
			Site currSite = regions[i].site;
			currSite.region = regions[i];

			if (!currSite.isBorder) {
				Site averageSite = new Site();

				for (int j = 0; j < currRegion.vertCount(); j++) {
					Vertex currVert = currRegion.getVerts().get(j);
					averageSite.x += currVert.x;
					averageSite.y += currVert.y;
					averageSite.z += currVert.z;
				}
				averageSite.x /= currRegion.vertCount();
				averageSite.y /= currRegion.vertCount();
				averageSite.z /= currRegion.vertCount();

				baseSites.set(i, averageSite);
			}
		}
	}

	public void mergeCornersAndEdges(double grid) {
		mergedVerts = voronoi.mergedVerts;// new LinkedHashMap<String,
											// Vertex>();
		mergedEdges = new LinkedHashMap<String, Edge>();

		for (int i = 0; i < regions.length; i++) {
			Region currRegion = regions[i];
			currRegion.site.region = currRegion;
			ArrayList<Vertex> verts = currRegion.getVerts();
			verts.add(0, currRegion.site);
			// Vertex currVert = verts.get(0); // adds the current site to
			// mergedVerts

			for (int j = 0; j < currRegion.vertCount(); j++) {
				Vertex currVert = currRegion.getVerts().get(j);
				if (mergedVerts.containsKey(currVert.toString())) {
					verts.set(j, mergedVerts.get(currVert.toString()));
					currVert = mergedVerts.get(currVert.toString());
				} else {
					mergedVerts.put(currVert.toString(), currVert);
				}
				if (!currVert.regions.contains(currRegion)) {
					currVert.regions.add(currRegion);
				}
			}
		}
		System.out.println("postmerge: " + mergedVerts.size());

		for (Map.Entry<String, Vertex> entry : mergedVerts.entrySet()) {
			Vertex currVert = (Vertex) entry.getValue();
			if (currVert.regions.size() == 0) {
				System.out.println("stop");
			}
		}
		for (int i = 0; i < edges.size(); i++) {
			Edge currEdge = edges.get(i);
			if (mergedVerts.containsKey(currEdge.vert1.toString())) {
				currEdge.vert1 = mergedVerts.get(currEdge.vert1.toString());
			}
			if (mergedVerts.containsKey(currEdge.vert2.toString())) {
				currEdge.vert2 = mergedVerts.get(currEdge.vert2.toString());
			}
			if (!currEdge.vert1.neighbors.contains(currEdge.vert2)) {
				currEdge.vert1.neighbors.add(currEdge.vert2);
			}
			if (!currEdge.vert2.neighbors.contains(currEdge.vert1)) {
				currEdge.vert2.neighbors.add(currEdge.vert1);
			}
			if (mergedEdges.containsKey(currEdge.toString())) {
				currEdge = mergedEdges.get(currEdge.toString());
			} else {
				mergedEdges.put(currEdge.toString(), currEdge);
			}
		}
		System.out.println("merged edges: " + mergedEdges.size() + "    " + edges.size());

		for (Map.Entry<String, Edge> entry : mergedEdges.entrySet()) {
			Edge currEdge = (Edge) entry.getValue();
			for (int j = 0; j < currEdge.vert1.regions.size(); j++) {
				for (int k = 0; k < currEdge.vert2.regions.size(); k++) {
					Region r1 = currEdge.vert1.regions.get(j);
					Region r2 = currEdge.vert2.regions.get(k);
					if (r1 == r2) {
						if (currEdge.site1 == null) {
							currEdge.site1 = r1.site;
						} else {
							currEdge.site2 = r1.site;
						}
					}
				}
			}
			if (currEdge.site1 != null) {
				currEdge.site1.edges.put(currEdge.site2, currEdge);
				if (currEdge.site2 != null) {
					currEdge.site2.edges.put(currEdge.site1, currEdge);
				}
			}
		}
	}

	public void makeLandorWater() {
		boolean isNoisy = true;

		for (int i = 0; i < regions.length; i++) {
			Region currRegion = regions[i];
			Site currSite = regions[i].site;

			ArrayList<Vertex> verts = currRegion.getVerts();

			int numWetCorners = 0;
			if (!isNoisy) {
				if (!currSite.isBorder) {
					for (int j = 0; j < verts.size(); j++) {
						Vertex currVert = verts.get(j);
						currVert.isWater = true;
						numWetCorners++;
					}
					double numWetEdges = numWetCorners / 2.0;
					if (numWetEdges > verts.size()) {
						currSite.isLake = true; // set anything wet as a lake by
						// default. We'll turn the
						// outside
						// ones to ocean next
					}
				} else {
					currSite.isLake = true;
					for (int j = 0; j < verts.size(); j++) {
						Vertex currVert = verts.get(j);
						currVert.isWater = true;
					}
				}
			} else {

				if (!currSite.isBorder) {
					// double noise = (float) n.fbm(currSite.x,
					// currSite.y, 0, 2, 0.925f, 200.0f);
					// double noise = n.Perlin_Noise((float) currSite.x /
					// 200000.0f, (float) currSite.y / 200000.0f, 0.10f, 4,
					// .85f, 0.55f);
					// noise *= n.Improved_Noise(currSite.x/30000.0,
					// currSite.y/30000.0, 0);
					// noise /= n.rmf(currSite.x / 50000.0, currSite.y /
					// 50000.0, 0, 4, .85f, 0.55f, 1.0f);

					numWetCorners = 0;
					double minNoise = Double.MAX_VALUE;
					double maxNoise = -Double.MAX_VALUE;
					for (int j = 0; j < verts.size(); j++) {
						Vertex currVert = verts.get(j);

						// double noise = n.Perlin_Noise((float) currVert.x /
						// 200000.0f, (float) currVert.y / 200000.0f, 0.10f, 4,
						// .85f, 0.55f);
						// noise /= n.rmf(currVert.x / 10000.0, currVert.y /
						// 10000.0, 0, 4, .85f, 0.55f, 1.0f);
						double X_off = currVert.x + r.nextDouble() * borderRadius;
						double Z_off = currVert.z + r.nextDouble() * borderRadius;
						double noise = n.Perlin_Noise((float) X_off / 100000.0f, (float) Z_off / 100000.0f, 0.10f, 20, .85f, 0.55f);
						// noise /= n.rmf(X_off / 10000.0, Z_off / 10000.0, 0,
						// 4, .85f, 0.55f, 1.0f);
						minNoise = Math.min(minNoise, noise);
						maxNoise = Math.max(maxNoise, noise);
						System.out.println("noise: " + Math.abs(noise) + "   " + minNoise + "   " + maxNoise + "   " + (minNoise + maxNoise) / 2.0);

						if (Math.abs(noise) < .631135) {
							currVert.isWater = true;
							// currEdge.corner1.alt = 0;
							numWetCorners++;
						} else {
							currVert.isWater = false;
							// currEdge.corner1.alt = Double.MAX_VALUE;
						}
					}
					// System.out.println("wet corners: " + numWetCorners +
					// "   " + currRegion.vertCount() / 2.0);
					if (numWetCorners > currRegion.vertCount() / 2.0) {
						currSite.isWater = true;
						currSite.isLake = true; // set anything wet is a lake by
						// default. We'll turn the outside
						// ones to ocean next
					}
				} else {
					currSite.isLake = true;
					for (int j = 0; j < verts.size(); j++) {
						Vertex currVert = verts.get(j);
						currVert.isWater = true;
					}
				}

			}
		}
	}

	public void expandBorder() {
		uncheckAllSites();
		for (int i = 0; i < regions.length; i++) {
			Region currRegion = regions[i];
			Site currSite = regions[i].site;

			if (currSite.isBorder && !currSite.checked) {
				currSite.isWater = true;
				for (Map.Entry<Site, Edge> entry : currSite.edges.entrySet()) {
					Site nextSite = (Site) entry.getKey();
					// for (int j = 0; j < currSite.links.size(); j++) {
					// Site nextSite = currSite.links.get(j);

					// if (nextSite.isBorder || currSite.isWater) {
					// currSite.isLake = true;
					// currSite.isWater = true;
					// }

					if (nextSite != null) { // a site is checked if it's a NEW
											// border
						nextSite.isBorder = true;
						nextSite.checked = true;
						nextSite.isWater = true;
					}
				}
			}
		}
	}

	public void uncheckAllSites() {
		for (int i = 0; i < baseSites.size(); i++) {
			Site currSite = (Site) baseSites.get(i);
			currSite.checked = false;
		}
	}

	public void divideLandAndWater() {
		uncheckAllSites();
		// cells = rawOutput.toCells();
		allRegions = new LinkedHashMap<Site, Region>();
		landRegions = new LinkedHashMap<Site, Region>();
		waterRegions = new LinkedHashMap<Site, Region>();

		Cell currCell;

		for (int i = 0; i < regions.length; i++) {
			Region currRegion = regions[i];
			allRegions.put(currRegion.site, currRegion);

			if (currRegion.site.isWater) {
				waterRegions.put(currRegion.site, currRegion);
			} else {
				landRegions.put(currRegion.site, currRegion);
			}
		}

		for (int i = 0; i < baseSites.size(); i++) {
			Site currSite = (Site) baseSites.get(i);
			currSite.checked = true;
			if (currSite.isBorder) {
				makeSelfAndWetNeighborsOcean(currSite);
				// assumes cells are all marked unchecked...
				return;
			}
		}
	}

	public void makeSelfAndWetNeighborsOcean(Site currSite) {
		currSite.isOcean = true;

		// MPolygon nextRegion = currRegion.findUncheckedNeighbor(cells);
		Site nextSite = currSite.nextUncheckedSite();

		while (nextSite != null) {
			nextSite.checked = true;
			if (nextSite.isWater) {
				makeSelfAndWetNeighborsOcean(nextSite);
			}
			nextSite = currSite.nextUncheckedSite();
		}
	}

	public void applyElevations() {
		for (int i = 0; i < baseSites.size(); i++) {
			Site currSite = (Site) baseSites.get(i);
			if (currSite.isWater || currSite.isBorder) {
				setAllEdgeAlts(currSite, 0.0f);
			} else {
				setAllEdgeAlts(currSite, Float.MAX_VALUE);
				for (int j = 0; j < currSite.links.size(); j++) {
					Site nextSite = currSite.links.get(j);
					Region currRegion = currSite.region;
					if ((nextSite.isOcean && !currSite.isOcean) || (currSite.isOcean && !nextSite.isOcean) && !currSite.isBorder) {
						if (currSite.edges.containsKey(nextSite)) {
							Edge currEdge = currSite.edges.get(nextSite);
							currEdge.vert1.y = 0;
							currEdge.vert2.y = 0;
						}
					}
				}
			}
		}

		int numChanged = 1;
		while (numChanged != 0) {
			numChanged = 0;
			ArrayList<Vertex> nextVerts = new ArrayList<Vertex>();
			for (Map.Entry<String, Vertex> entry : mergedVerts.entrySet()) {
				Vertex currVert = (Vertex) entry.getValue();

				ArrayList<Vertex> neighbors = currVert.neighbors;
				double minAlt = currVert.y;
				for (int j = 0; j < neighbors.size(); j++) {
					minAlt = Math.min(minAlt, neighbors.get(j).y);
				}
				if (minAlt + 1 < currVert.y) {
					if (!currVert.isWater) {
						currVert.y = minAlt + 1;
						numChanged++;
					} else {
						currVert.y = minAlt;
					}
				}
				nextVerts.add(currVert);
			}
			// corners = nextVerts;
		}
		maxAlt = 0;
		for (int i = 0; i < edges.size(); i++) {
			if (edges.get(i).vert1.y == Float.MAX_VALUE || edges.get(i).vert2.y == Float.MAX_VALUE) {
				System.out.println(edges.get(i).vert1.y + "   " + edges.get(i).vert2.y);
			}
			maxAlt = Math.max(maxAlt, edges.get(i).vert1.y);
			maxAlt = Math.max(maxAlt, edges.get(i).vert2.y);
		}

	}

	public void setAllEdgeAlts(Site currSite, float alt) {
		Region currRegion = currSite.region;
		ArrayList<Vertex> verts = currRegion.getVerts();

		for (int i = 0; i < verts.size(); i++) {
			verts.get(i).y = alt;
		}
		currSite.y = alt;
	}

	public void scaleElevations(double scaleFactor) {
		double erosion = 0.0; // 0 = none, 0.175 = good, 1.0 = lots, >1.0 =
								// waaaaayyyyy too much
		for (int i = 0; i < baseSites.size(); i++) {
			Site currSite = (Site) baseSites.get(i);

			Region currRegion = currSite.region;
			ArrayList<Vertex> verts = currRegion.getVerts();

			for (int j = 0; j < verts.size(); j++) {
				if (!verts.get(j).checked) {
					verts.get(j).checked = true;
					// verts.get(j).y *= scaleFactor; // linear scale
					if (verts.get(j).y > 0) {
						verts.get(j).y = (scaleFactor * verts.get(j).y) / Math.exp(erosion * (maxAlt - verts.get(j).y));
					}
				}
			}
		}
		for (int i = 0; i < baseSites.size(); i++) {
			Site currSite = (Site) baseSites.get(i);
			Region currRegion = currSite.region;
			ArrayList<Vertex> verts = currRegion.getVerts();

			float avg_Alt = 0;
			float max_Alt = 0;
			for (int j = 1; j < verts.size(); j++) {
				avg_Alt += verts.get(j).y;
				max_Alt = (float) Math.max(max_Alt, verts.get(j).y);
			}
			avg_Alt /= (verts.size() - 1);
			verts.get(0).y = currSite.y = avg_Alt;
		}
	}

	public void computeNormals() {
		for (int i = 0; i < baseSites.size(); i++) {
			Site currSite = (Site) baseSites.get(i);

			Region currRegion = currSite.region;

			currRegion.computeNormal();
		}
		System.out.println("compute normals");

		for (Map.Entry<String, Vertex> entry : mergedVerts.entrySet()) {
			Vertex currVert = (Vertex) entry.getValue();
			if (!(currVert instanceof Site)) {
				Normal avgNormal = new Normal();
				for (int i = 0; i < currVert.regions.size(); i++) {
					Site currSite = currVert.regions.get(i).site;
					avgNormal.x += currSite.normal.x;
					avgNormal.y += currSite.normal.y;
					avgNormal.z += currSite.normal.z;
				}
				avgNormal.x /= currVert.regions.size();
				avgNormal.y /= currVert.regions.size();
				avgNormal.z /= currVert.regions.size();
				currVert.normal = avgNormal;
				avgNormal.normalize();
			}
		}

	}

	public void glScale() {
		for (int i = 0; i < regions.length; i++) {
			Region currRegion = regions[i];
			for (int j = 0; j < currRegion.vertCount(); j++) {
				Vertex currCoords = currRegion.getVerts().get(j);
				currCoords.checked = false;
			}
		}
		for (int i = 0; i < regions.length; i++) {
			Region currRegion = regions[i];
			Site currSite = regions[i].site;

			// currSite.x *= GameLogic.GL_SCALE;
			// currSite.y *= GameLogic.GL_SCALE;
			// if (currSite.y > 20000) {
			// System.out.println("too tall!");
			// }
			// currSite.z *= GameLogic.GL_SCALE;
			for (int j = 0; j < currRegion.vertCount(); j++) {
				Vertex currCoords = currRegion.getVerts().get(j);
				if (!currCoords.checked) {
					currCoords.x *= GameLogic.GL_SCALE;
					currCoords.y *= GameLogic.GL_SCALE;
					currCoords.z *= GameLogic.GL_SCALE;
					currCoords.checked = true;
				}
			}
		}
	}

/*
 * 
 * public void determineDownHill() {
 * for (Map.Entry<Site, Cell> entry : cells.entrySet()) {
 * LandscapeSite currSite = (LandscapeSite) entry.getKey();
 * Cell currCell = (Cell) entry.getValue();
 * for (int i = 0; i < currCell.edges.size(); i++) {
 * GraphEdge currEdge = currCell.edges.get(i);
 * if (currEdge.corner1.alt == 0) {
 * currEdge.corner1.downhillCorner = null;
 * } else {
 * currEdge.corner1.downhillCorner = currEdge.corner1.findLowestNeighbor();
 * }
 * if (currEdge.corner2.alt == 0) {
 * currEdge.corner2.downhillCorner = null;
 * } else {
 * currEdge.corner2.downhillCorner = currEdge.corner2.findLowestNeighbor();
 * }
 * }
 * }
 * }
 * 
 * public void placeRivers(int numRivers) {
 * Random generator = new Random();
 * Object[] cellArray = landCells.values().toArray();
 * for (int i = 0; i < numRivers; i++) {
 * int randomIndex = Math.abs(generator.nextInt(cellArray.length));
 * Cell randomCell = (Cell) cellArray[randomIndex];
 * GraphEdge randomEdge =
 * randomCell.edges.get(generator.nextInt(randomCell.edges.size()));
 * Corner lowCorner;
 * if (randomEdge.corner1.alt < randomEdge.corner2.alt) {
 * recurseRiver(randomEdge.corner1, 1);
 * } else {
 * recurseRiver(randomEdge.corner2, 1);
 * }
 * }
 * }
 * 
 * public void recurseRiver(Corner lowCorner, int width) {
 * Corner nextCorner = lowCorner.downhillCorner;
 * lowCorner.weight += width;
 * // lowCorner.alt = Math.max(0,lowCorner.alt-1);
 * 
 * if (nextCorner != null && !lowCorner.isWater) {
 * recurseRiver(nextCorner, width + 1);
 * } else {
 * }
 * }
 * 
 * public void makeGLTriangleFans(){
 * for (Map.Entry<Site, Cell> entry : cells.entrySet()) {
 * Cell currCell = (Cell) entry.getValue();
 * 
 * currCell.makeTriangleFan();
 * }
 * }
 */
}
